import { mergeStatus, QuadraStatus, Status, StatusKey } from "./status"
import { DealerEntry, DealerKey } from "./entries/dealer"
import { SkillContext, SkillFrame } from "@/core/skill"
/**
 * @Author: Kritsu
 * @Date:   2021/11/09 16:18:33
 * @Last Modified by:   Kritsu
 * @Last Modified time: 2021/11/17 18:35:28
 */
import { createSkillColumn, createSkillContext, Skill, SkillColumn, SkillResult } from "@/core/skill"
import { ArmorType } from "@/core/equips"
import { createCharacterStatus, mergeStatusOptions, StatusOptions } from "@/core/status"
import baseStatus from "@/assets/data/base_status.json"
import { CalcData, CalcResult } from "./calc"
import { useCharacterStore } from "@/store"
import { use_dungeon_buff } from "./dungeon_buff"
import attack_targets from "./attack_targets"

interface StatusData {
    [key: string]: number[]
}

export class Character {
    constructor({ job, alter, care_status = {}, skills = [], adapt_status = "intelligence", is_buffer, dealer_type, armor_type }: CharacterOptions) {
        this.job = job
        this.alter = alter
        this.care_status = care_status
        this.adapt_status = adapt_status
        this.dealer_type = dealer_type
        this.is_buffer = is_buffer

        this.armor_type = armor_type
        this.skills = skills
            .sort((a, b) => a.type - b.type)
            .map(e => {
                return {
                    ...e,
                    name: this.get_name(e.name)
                } as Skill
            })
            .map(createSkillColumn)
    }

    get_name(name: string) {
        return [this.job, this.alter, name].join(".")
    }

    //职业
    readonly job: string

    readonly alter: string

    readonly care_status: CareStatus

    readonly adapt_status: StatusKey

    readonly dealer_type: DealerType

    readonly is_buffer: boolean

    readonly level: number = 100

    readonly skills: SkillColumn[]

    readonly armor_type: ArmorType;

    [key: string]: any

    compute_buffer(array: CalcData[]): CalcResult {
        const skills = this.skills

        const useSkill = (name: string) => {
            const column = skills.find(e => {
                let skill_name = e.skill.name
                return skill_name == name || skill_name == this.get_name(name)
            })
            if (column) {
                return () => forceSkill(column)
            }
        }

        const used_skills = new Map<string, SkillResult>()

        function forceSkill(column: SkillColumn): SkillResult {
            const skill_name = column.skill.name
            let result = used_skills.get(skill_name)
            if (!result) {
                const skillContext = context_map[column.skill.name]
                const rs = column.skill.effect(skillContext, context) ?? {}

                const frames: SkillFrame[] = (rs?.frames ?? []).map(e => {
                    return {
                        hits: e.hits,
                        attack: e.attack.multiply(skillContext.attack)
                    }
                })

                result = {
                    adapt_number: 0,
                    damage: 0,
                    value: {},
                    ...rs,
                    name: column.skill.name,
                    level: rs?.level ?? skillContext.level,
                    frames
                }
                used_skills.set(skill_name, result)
            }

            return result
        }

        const status = createBaseStatus(this.job, this.alter, this.level)
        const context = new CalcData({ status })

        for (let item of array) {
            context.apply(item)
        }

        const useSkillContext = createSkillContext(
            {
                skill_name: "",
                level: 0,
                adapt_number: 0,
                cdr: 1,
                attack: 1,
                add_level: 0,
                useSkill
            },
            context
        )

        const useSkillFrame = this.createDamage(context)

        const context_map: { [skill_name: string]: SkillContext } = {}

        const skill_datas: SkillResult[] = []

        let final_results = createCharacterStatus()

        for (let column of skills) {
            let skillContext = useSkillContext(column)

            skillContext.adapt_number = context.status[this.adapt_status].plus(context.dungeon_status[this.adapt_status])

            context_map[column.skill.name] = skillContext

            let { value = {}, level, name, frames, adapt_number = 0 } = forceSkill(column)

            // 计算每一个伤害帧

            value = createCharacterStatus(value)
            const damage = useSkillFrame(frames)

            mergeStatusOptions(final_results, value)

            skill_datas.push({ name, value, level, frames, adapt_number, damage })
        }

        const rate = compute_rate(final_results)

        return { context, final_results, rate, skill_datas }
    }

    //伤害指数
    createDamage({ status, deal_entry }: CalcData) {
        const { base_setting } = useCharacterStore()
        const { ice_boost, fire_boost, light_boost, dark_boost } = status
        const { intstr_per, triatk_per, critical_damage, bonus_damage, bonus_elemental_damage, skill_attack, standalone_damage, final_damage, additional_damage, continuous_damage } = deal_entry

        const { phydefense, magdefense } = attack_targets

        const defense = this.dealer_type.startsWith("phy") ? phydefense : magdefense
        const defense_rate = defense.divide(defense.multiply(200))

        let [power, atk] = get_core_status(status, this.dealer_type)

        power = use_dungeon_buff(base_setting.dungeon_buff, power)

        let elemental_boost = Math.max(ice_boost, fire_boost, light_boost, dark_boost, 0)

        //属性伤害加成
        const elemental_damage = elemental_boost.divide(220)

        power = power.multiply(intstr_per.plus(1)) // 百分比力智加成

        atk = atk.multiply(triatk_per.plus(1)) // 百分比三攻加成

        let damage = compute_attack(power, atk) // 计算攻击力

        damage = damage.minus(damage.multiply(defense_rate)) // 减伤

        damage = damage.multiply(1.5).multiply(critical_damage.plus(1)) // 暴击伤害

        damage = damage.multiply(skill_attack.plus(1)) // 技能攻击力

        damage = damage.multiply(standalone_damage.plus(1)) // 药剂增伤

        damage = damage.multiply(elemental_damage.plus(0.05).plus(1)) // 属性伤害

        damage = damage.multiply(additional_damage.plus(1)) // 额外伤害

        damage = damage.multiply(continuous_damage.plus(1)) // 持续伤害

        damage = damage.multiply(final_damage.plus(1)) // 最终伤害

        damage = damage.multiply(bonus_damage.plus(bonus_elemental_damage.perMulti(elemental_damage)).plus(1)) //附加伤害

        damage = damage.divide(1000).divide(100) // 伤害压缩

        return (frames: SkillFrame[]) => {
            let result = 0

            for (let frame of frames) {
                let { attack, hits } = frame
                result = result.plus(attack.multiply(damage).multiply(hits))
            }

            return result
        }
    }
}

//计算伤害
function compute_damage(frames: SkillFrame[]) {
    const { base_setting } = useCharacterStore()
    const { apc, dungeon_buff } = base_setting
}

function get_core_status(status: Status, type: DealerType) {
    const { strength, intelligence, phyatk, magatk, indatk } = status
    let atk = 0
    let power = 0
    switch (type) {
        case "phyatk":
            atk = phyatk
            power = strength
            break
        case "magatk":
            atk = magatk
            power = intelligence

            break
        case "phyind":
            atk = indatk
            power = strength

            break
        case "magind":
            atk = indatk
            power = intelligence
            break
    }
    return [power, atk]
}

function compute_rate(result: Status) {
    const { base_setting } = useCharacterStore()
    const { apc, dungeon_buff } = base_setting
    let [power, atk] = get_core_status(result, apc.type)

    const dungeon = use_dungeon_buff(dungeon_buff, apc.intstr)
    const x0 = compute_attack(apc.intstr.plus(dungeon), apc.attack)
    const x1 = compute_attack(apc.intstr.plus(dungeon).plus(power), atk.plus(apc.attack))
    return x1.divide(x0)
}

function compute_attack(intstr: number, attack: number) {
    return intstr.divide(250).plus(1).multiply(attack)
}

export interface CareStatus extends StatusOptions {}

export type DealerType = "phyatk" | "magatk" | "phyind" | "magind"

export interface CharacterOptions {
    alter: string
    job: string
    armor_type: ArmorType
    adapt_status?: QuadraStatus
    dealer_type: DealerType
    care_status: CareStatus
    skills: Skill[]
    is_buffer: boolean
}

export function createBaseStatus(job: string, alter: string, level = 100) {
    let awaken = 0
    if (level >= 75) {
        awaken = 275
    } else if (level >= 15) {
        awaken = 145
    }
    const statusData = baseStatus as StatusData
    const alterData = statusData[alter]
    const jobData = statusData[job]

    const level0 = Math.min(14, level - 1)
    const level1 = Math.max(level - 15, 0)
    const level2 = Math.min(71, level)
    let strength = jobData[4].plus(jobData[0].multiply(level0).plus(alterData[0].multiply(level1)).plus(awaken).plus(level2.multiply(2.1)))
    let intelligence = jobData[5].plus(jobData[1].multiply(level0).plus(alterData[1].multiply(level1)).plus(awaken).plus(level2.multiply(2)))
    let vitality = jobData[6].plus(jobData[2].multiply(level0).plus(alterData[2].multiply(level1)).plus(awaken).plus(level2.multiply(2.1)))
    let spirit = jobData[7].plus(jobData[3].multiply(level0).plus(alterData[3].multiply(level1)).plus(awaken).plus(level2.multiply(2)))

    return createCharacterStatus({
        strength,
        intelligence,
        spirit,
        vitality,
        phyatk: 65,
        magatk: 65,
        indatk: 1045
    })
}
